
package com.eworking.eim.receive;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;

import com.eworking.eim.EIMConfig;
import com.eworking.eim.message.EIMMessage;
import com.eworking.eim.message.EIMMessageEncoder;
import com.eworking.eim.message.body.FileMessageBody;
import com.eworking.eim.packet.EIMCustomPacketExtension;
import com.eworking.eim.packet.EIMEncryptPacketExtension;
import com.eworking.eim.util.EIMLog;
import com.eworking.eim.util.PathUtil;
import com.eworking.eim.xmpp.XMPPConnectionManager;

import android.text.TextUtils;

public class EIMMessageListener implements PacketListener {
    
    /** 日志输出标记 */
    private static final String _TAG = EIMMessageListener.class.getSimpleName();
    
    /** 消息接收线程集合 */
    protected ExecutorService receiveThreadPool = null;
    
    /** 最近的消息集合 */
    protected ArrayBlockingQueue<String> recentMsgIdQueue = null;
    
    /** 保存最近消息数量 */
    protected static final int RECENT_QUEUE_SIZE = 20;
    
    /** 接收消息的最近发送人 - 用于判断是否重复消息 */
    protected String previousFrom = null;
    
    /** 接收消息的最近发送內容 - 用于判断是否重复消息 */
    protected String previousBody = null;
    
    /** 接收消息的最近发送時間 - 用于判断是否重复消息 */
    protected long previousTime = 0;
    
    public EIMMessageListener() {
        this.receiveThreadPool = Executors.newCachedThreadPool();
        this.recentMsgIdQueue = new ArrayBlockingQueue<String>(RECENT_QUEUE_SIZE);
    }
    
    @Override
    public synchronized void processPacket(Packet packet) {
        Message message = (Message) packet;
        // 处理接收的消息
        this.processMessage(message);
    }
    
    protected void processMessage(Message message) {
        // 发送接收消息回执
        this.ackMessage(message);
        if (!TextUtils.isEmpty(message.getBody())) {
            // 是否存在重复的消息
            if (!this.isDuplicateMessage(message)) {
                EIMMessage eimMessage = EIMMessageEncoder.decryptMessage(message);
                if (message.getExtension(EIMEncryptPacketExtension._NAME, EIMEncryptPacketExtension._NAME_SPACE) != null) {
                    // 设置属性为加密
                    eimMessage.setAttribute(EIMMessage._ATTR_ENCRYPTED, true);
                }
                this.processEIMMessage(eimMessage);
            }
        }
    }
    
    /**
     * 处理接收消息
     * 
     * @param message
     */
    protected void processEIMMessage(EIMMessage message) {
        try {
            if (message.type == EIMMessage.Type.CMD) {
                
                return;
            }
            // 带有附件的消息，图片、附件、语音、视频等
            if (message.messageBody instanceof FileMessageBody) {
                // 设置本地文件地址
                setLocalURL(message);
                boolean isEncrypted = message.getBooleanAttribute(EIMMessage._ATTR_ENCRYPTED);
                // 启动线程处理附件
                EIMReceiveMessageThread receiveMessageThread = new EIMReceiveMessageThread(message, isEncrypted);
                this.receiveThreadPool.execute(receiveMessageThread);
            }
        } catch (Exception e) {
        }
    }
    
    /**
     * 设置本地文件地址
     * 
     * @param message
     */
    protected void setLocalURL(EIMMessage message) {
        FileMessageBody fileMessageBody = (FileMessageBody) message.messageBody;
        // 文件名
        String filename = fileMessageBody.remoteUrl.substring(fileMessageBody.remoteUrl.lastIndexOf("/") + 1);
        if (message.type == EIMMessage.Type.IMAGE) {
            fileMessageBody.localUrl = PathUtil.getInstance().getImagePath().getPath() + "/" + filename;
        } else if (message.type == EIMMessage.Type.VOICE) {
            fileMessageBody.localUrl = PathUtil.getInstance().getVoicePath().getPath() + "/" + filename;
        } else if (message.type == EIMMessage.Type.VIDEO) {
            fileMessageBody.localUrl = PathUtil.getInstance().getVideoPath().getPath() + "/" + filename;
        } else {
            fileMessageBody.localUrl = PathUtil.getInstance().getVoicePath().getPath() + "/" + filename;
        }
    }
    
    /**
     * 判断是否重复发的消息，【1秒内相同发送人、相同的内容】 定义为重复的消息
     * 
     * @param message
     * @return
     */
    protected boolean isDuplicateMessage(Message message) {
        if (message.getFrom().equals(previousFrom) && message.getBody().equals(previousBody)
            && System.currentTimeMillis() - previousTime > 1000) {
            EIMLog.d(_TAG, "接收到重复消息，不处理" + message.getPacketID());
            return true;
        }
        this.previousBody = message.getBody();
        this.previousFrom = message.getFrom();
        this.previousTime = System.currentTimeMillis();
        for (String msgId : this.recentMsgIdQueue) {
            if (msgId.equals(message.getPacketID())) {
                EIMLog.d(_TAG, "接收到重复消息，不处理" + message.getPacketID());
                return true;
            }
        }
        if (this.recentMsgIdQueue.size() == 20) {
            this.recentMsgIdQueue.poll();
        }
        // 放入最近消息集合中
        this.recentMsgIdQueue.add(message.getPacketID());
        return false;
    }
    
    /**
     * 接收消息后，发送消息回执给服务器
     * 
     * @param message
     */
    protected void ackMessage(Message message) {
        String msgId = message.getPacketID();
        if (!TextUtils.isEmpty(msgId)) {
            Message ackMessage = new Message();
            ackMessage.setPacketID(msgId);
            ackMessage.setFrom(message.getTo());
            // 服务器地址
            ackMessage.setTo(EIMConfig.DOMAIN);
            EIMCustomPacketExtension customPacketExtension =
                new EIMCustomPacketExtension(EIMCustomPacketExtension._RECEIVED);
            customPacketExtension.setValue(EIMCustomPacketExtension._ID, msgId);
            ackMessage.addExtension(customPacketExtension);
            // 发送消息
            XMPPConnectionManager.getInstance().getConnection().sendPacket(ackMessage);
        }
    }
}
